package org.budo.druid.filter.factory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.budo.druid.filter.datasource.timeout.DataSourceGetConnectionTimeoutFilter;
import org.budo.druid.filter.driver.connect.error.DriverConnectErrorFilter;
import org.budo.druid.filter.log.slf4j.BudoSlf4jLogFilter;
import org.budo.druid.filter.statement.timeout.StatementQueryTimeoutFilter;
import org.budo.druid.filter.timestamp.TimestampErrorFilter;
import org.budo.druid.filter.wall.BudoWallFilterFactoryBean;
import org.budo.graph.druid.filter.stat.BudoStatFilter;
import org.budo.support.lang.util.ReflectUtil;
import org.springframework.beans.factory.FactoryBean;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.logging.LogFilter;
import com.alibaba.druid.filter.logging.Slf4jLogFilter;
import com.alibaba.druid.filter.stat.StatFilter;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * @author limingwei
 */
@Slf4j
@Getter
@Setter
public class DefaultDruidFiltersFactoryBean implements FactoryBean<List<Filter>> {
    private List<Filter> druidFilters = null;

    private Integer statementQueryTimeout = 5;

    private Boolean wallFilter = true;

    private Boolean budoStatFilter = true;

    private Boolean druidStatFilter = false;

    private Boolean logFilter = true;

    private Boolean mergeSql = false;

    private Boolean stackTraceWhenError = true;

    public List<Filter> getObject() throws Exception {
        if (null != this.druidFilters) {
            return this.druidFilters;
        }

        synchronized (DefaultDruidFiltersFactoryBean.class) {
            if (null != this.druidFilters) {
                return this.druidFilters;
            }

            return this.druidFilters = this.initDruidFilters();
        }
    }

    private List<Filter> initDruidFilters() {
        List<Filter> defaultFilters = new ArrayList<Filter>(Arrays.asList( //
                this.logFilter(), //
                new StatementQueryTimeoutFilter(this.getStatementQueryTimeout()), //
                new DataSourceGetConnectionTimeoutFilter(), //
                new DriverConnectErrorFilter(), //
                new TimestampErrorFilter() //
        ));

        if (this.getWallFilter()) {
            defaultFilters.add(this.newWallFilter());
        }

        if (this.getBudoStatFilter()) {
            if (ReflectUtil.hasClass("org.budo.graph.druid.filter.stat.BudoStatFilter")) {

                BudoStatFilter budoStatFilter = new BudoStatFilter();
                budoStatFilter.setMergeSql(this.getMergeSql());
                defaultFilters.add(budoStatFilter); // BudoStatFilter
            } else {
                log.error("#80 class not found org.budo.graph.druid.filter.stat.BudoStatFilter");
            }
        }

        if (this.getDruidStatFilter()) {
            StatFilter statFilter = new StatFilter();
            statFilter.setMergeSql(this.getMergeSql());
            defaultFilters.add(statFilter);
        }

        if (this.getLogFilter()) {
            defaultFilters.add(this.logFilter());
        }

        return defaultFilters;
    }

    protected Filter newWallFilter() {
        return BudoWallFilterFactoryBean.budoWallFilter();
    }

    private LogFilter logFilter() {
        // statement_executeErrorAfter
        // Table 'tj_hawkeye.tj_customer_screen_capture' doesn't exist

        Slf4jLogFilter logFilter = (null == this.stackTraceWhenError || this.stackTraceWhenError) //
                ? new Slf4jLogFilter() //
                : new BudoSlf4jLogFilter();

        logFilter.setStatementExecutableSqlLogEnable(true);

        logFilter.setStatementCloseAfterLogEnabled(false);
        logFilter.setStatementExecuteAfterLogEnabled(false);
        logFilter.setStatementPrepareAfterLogEnabled(false);
        logFilter.setStatementParameterSetLogEnabled(false);
        return logFilter;
    }

    public Class<?> getObjectType() {
        return List.class;
    }

    public boolean isSingleton() {
        return true;
    }
}
